/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sirtet.tetris.generator;

import java.util.Iterator;
import java.util.List;

/**
 *
 * @author yunglu
 */
public class BlokusPiece {

    public BlokusPiece(boolean[][] matrix) {
        squares = copy(matrix);
    }

    public boolean getSquare(int r, int c) {
        if ((r < 0) || (r >= squares.length)) {
            return false;
        }
        if ((c < 0) || (c >= squares[0].length)) {
            return false;
        }
        return squares[r][c];
    }

    public void print() {
        int row = squares.length;
        int col = squares[0].length;
        System.out.println(" ");
        for (int r = 0; r < row; r++) {
            for (int c = 0; c < col; c++) {
                if (squares[r][c] == false) {
                    System.out.print(" ");
                } else {
                    System.out.print("S");
                }
            }
            System.out.println(" ");
        }
        System.out.println(" ");
    }

    public static BlokusPiece copy(BlokusPiece bp) {
        return new BlokusPiece(BlokusPiece.copy(bp.squares));
    }
    private static boolean[][] copy(boolean[][] squares) {
        int row = squares.length;
        int col = squares[0].length;
        boolean[][] variation = new boolean[row][col];
        for (int r = 0; r < row; r++) {
            for (int c = 0; c < col; c++) {
                variation[r][c] = squares[r][c];
            }
        }
        return variation;
    }

    private static boolean[][] mirrorV(boolean[][] squares) {
        // vertical mirror
        int row = squares.length;
        int col = squares[0].length;
        boolean[][] variation = new boolean[row][col];
        for (int r = 0; r < row; r++) {
            for (int c = 0; c < col; c++) {
                variation[r][c] = squares[r][col - c - 1];
            }
        }
        return variation;
    }

    private static boolean[][] mirrorH(boolean[][] squares) {
        // horizontal mirror
        int row = squares.length;
        int col = squares[0].length;
        boolean[][] variation = new boolean[row][col];
        for (int r = 0; r < row; r++) {
            for (int c = 0; c < col; c++) {
                variation[r][c] = squares[row - r - 1][c];
            }
        }
        return variation;
    }

    private static boolean[][] rotate(boolean[][] squares) {
        // rotate clockwise by 90 degrees
        int col = squares.length;
        int row = squares[0].length;
        boolean[][] variation = new boolean[row][col];
        for (int r = 0; r < row; r++) {
            for (int c = 0; c < col; c++) {
                variation[r][c] = squares[col - c - 1][r];
            }
        }
        return variation;
    }

    public static boolean isIdentical(boolean[][] var1,
            boolean[][] var2) {
        // isIdentical means exactly the same (same orientation)
        int row = var1.length;
        int col = var1[0].length;
        if (row != var2.length) {
            return false;
        }
        if (col != var2[0].length) {
            return false;
        }
        for (int r = 0; r < row; r++) {
            for (int c = 0; c < col; c++) {
                if (var1[r][c] != var2[r][c]) {
                    return false;
                }
            }
        }
        return true;
    }

    public boolean equivalent(BlokusPiece bp) {
        // equivalent means two pieces are 
        // identical without or with transformation
        if (isIdentical(squares, bp.squares) == true) {
            return true;
        }
        boolean[][] var;
        for (int bv = 1; bv <= 7; bv++) {
            // 7 variations
            boolean rot = ((bv & 4) > 0);
            boolean mirv = ((bv & 2) > 0);
            boolean mirh = ((bv & 1) > 0);
            // Disallow mirror,  therefore set mirv=mirh
            mirv = mirh;
            var = transform(bp.squares, rot, mirv, mirh);
            if (isIdentical(squares, var) == true) {
                return true;
            }
        }
        return false;
    }

    public static boolean[][] transform(boolean[][] squares,
            boolean rot, boolean mirv, boolean mirh) {
        // rotation, vertical mirror, and horizontal mirror
        if ((rot || mirv || mirh) == false) {
            // no transformation needed
            return squares;
        }
        boolean[][] variation = null;
        if (rot == true) {
            variation = rotate(squares);
        }
        if (mirv == true) {
            if (variation == null) {
                variation = mirrorV(squares);
            } else {
                variation = mirrorV(variation);
            }
        }
        if (mirh == true) {
            if (variation == null) {
                variation = mirrorH(squares);
            } else {
                variation = mirrorH(variation);
            }
        }
        return variation;
    }

    private static void shiftRows(final List<Integer> pList,
            RowPosition parent, int row) {
        if (row == (pList.size())) {
            // all rows have been shifted
            return;
        }
        // shift this row relative to the last row
        // create all possible cases
        int lastShift = parent.getShift();
        int lastLen = pList.get(row - 1).intValue();
        int thisLen = pList.get(row).intValue();
        int minShift = lastShift - thisLen + 1;
        int maxShift = lastShift + lastLen - 1;
        RowPosition firstChild = new RowPosition(row, minShift, parent, null);
        RowPosition currChild = firstChild;
        for (int pos = minShift + 1; pos <= maxShift; pos++) {
            RowPosition nextChild = new RowPosition(row, pos, parent, currChild);
            currChild = nextChild;
        }
        // progress to the next row
        currChild = firstChild;
        while (currChild != null) {
            shiftRows(pList, currChild, row + 1);
            currChild = currChild.getSibling();
        }
    }

    public static List<boolean[][]> createMatrix(final List<Integer> pList) {
        if (pList.size() == 1) {
            // divide the number into a single integer. 
            // This is the rotation of dividing the number into the sum of 1's 
            // This must be redundant so it is ignored.
            return null;
        }
        // pList has at least two rows
        Iterator iter = pList.iterator();
        int maxShift = 0; // how many squares the top row needs to shift
        iter.next(); // ignore the first row
        while (iter.hasNext()) {
            Integer rowLength = (Integer) iter.next();
            maxShift += rowLength.intValue() - 1;
        }
        RowPosition topRow = new RowPosition(0, maxShift, null, null);
        shiftRows(pList, topRow, 1);
        return topRow.createMatrixList(pList);
    }
    boolean[][] squares;
} 
